package niu.toy.spring.mvc;

import cn.hutool.core.lang.Assert;

import lombok.Data;
import niu.toy.spring.ioc.annotation.Nullable;
import niu.toy.spring.support.ParameterNameDiscoverer;


import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * 封装Action方法参数
 */
@Data
public class HandlerMethodParameter {
    private  int parameterIndex;
    private final Executable executable;

    private volatile Parameter parameter;

    @Nullable
    private volatile ParameterNameDiscoverer parameterNameDiscoverer;

    @Override
    public HandlerMethodParameter clone() {
        return new HandlerMethodParameter(this);
    }
    public HandlerMethodParameter(HandlerMethodParameter original) {
        Assert.notNull(original, "Original must not be null");
        this.executable = original.executable;
        this.parameterIndex = original.parameterIndex;
        this.parameter = original.parameter;
    }

    public HandlerMethodParameter(Method method, int parameterIndex) {
        Assert.notNull(method, "Method must not be null");
        this.executable = method;
        this.parameterIndex = parameterIndex;
    }
    public void initParameterNameDiscovery(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {
        this.parameterNameDiscoverer = parameterNameDiscoverer;
    }
    @Nullable
    public String getParameterName() {
        ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
        if (discoverer != null) {
            String[] parameterNames = null;
            if (this.executable instanceof Method) {
                parameterNames = discoverer.getParameterNames((Method) this.executable);
            }
            else if (this.executable instanceof Constructor) {
                parameterNames = discoverer.getParameterNames((Constructor<?>) this.executable);
            }
            if (parameterNames != null) {
               return parameterNames[this.parameterIndex];
            }
        }
        return null;
    }
    public Class<?> getParameterType(){
        return this.parameter.getType();
    }
    /**
     *
     * @param annotationType
     * @param <A>
     * @return
     */
    public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) {
        return this.executable.isAnnotationPresent(annotationType);
    }

    public <A extends Annotation> boolean hasParameterAnnotation(Class<A> annotationType) {
        return  this.parameter.isAnnotationPresent(annotationType);
    }

    public Method getMethod() {
        return (this.executable instanceof Method ? (Method) this.executable : null);
    }

}
